/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                        Intel License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/


#include "precomp.hpp"
#include "cv.h"

// Original implementation by   Mark Asbach
//                              Institute of Communications Engineering
//                              RWTH Aachen University
//
// For implementation details and background see:
// http://developer.apple.com/samplecode/qtframestepper.win/listing1.html
//
// Please note that timing will only be correct for videos that contain a visual track
// that has full length (compared to other tracks)


// standard includes
#include <cstdio>
#include <cassert>

// Mac OS includes
#include <Carbon/Carbon.h>
#include <CoreFoundation/CoreFoundation.h>
#include <QuickTime/QuickTime.h>


// Global state (did we call EnterMovies?)
static int did_enter_movies = 0;

// ----------------------------------------------------------------------------------------
#pragma mark Reading Video Files

/// Movie state structure for QuickTime movies
typedef struct CvCapture_QT_Movie {
	Movie      myMovie;            // movie handle
	GWorldPtr  myGWorld;           // we render into an offscreen GWorld

	CvSize     size;               // dimensions of the movie
	TimeValue  movie_start_time;   // movies can start at arbitrary times
	long       number_of_frames;   // duration in frames
	long       next_frame_time;
	long       next_frame_number;

	IplImage* image_rgb;           // will point to the PixMap of myGWorld
	IplImage* image_bgr;           // will be returned by icvRetrieveFrame_QT()

} CvCapture_QT_Movie;


static       int         icvOpenFile_QT_Movie      (CvCapture_QT_Movie* capture, const char*   filename);
static       int         icvClose_QT_Movie         (CvCapture_QT_Movie* capture);
static       double      icvGetProperty_QT_Movie   (CvCapture_QT_Movie* capture, int property_id);
static       int         icvSetProperty_QT_Movie   (CvCapture_QT_Movie* capture, int property_id, double value);
static       int         icvGrabFrame_QT_Movie     (CvCapture_QT_Movie* capture);
static const void*       icvRetrieveFrame_QT_Movie (CvCapture_QT_Movie* capture, int);


static CvCapture_QT_Movie* icvCaptureFromFile_QT (const char* filename) {
	static int did_enter_movies = 0;
	if (! did_enter_movies) {
		EnterMovies();
		did_enter_movies = 1;
	}

	CvCapture_QT_Movie* capture = 0;

	if (filename) {
		capture = (CvCapture_QT_Movie*) cvAlloc (sizeof (*capture));
		memset (capture, 0, sizeof(*capture));

		if (!icvOpenFile_QT_Movie (capture, filename)) {
			cvFree( &capture );
		}
	}

	return capture;
}



/**
 * convert full path to CFStringRef and open corresponding Movie. Then
 * step over 'interesting frame times' to count total number of frames
 * for video material with varying frame durations and create offscreen
 * GWorld for rendering the movie frames.
 *
 * @author Mark Asbach <asbach@ient.rwth-aachen.de>
 * @date   2005-11-04
 */
static int icvOpenFile_QT_Movie (CvCapture_QT_Movie* capture, const char* filename) {
	Rect          myRect;
	short         myResID        = 0;
	Handle        myDataRef      = nil;
	OSType        myDataRefType  = 0;
	OSErr         myErr          = noErr;


	// no old errors please
	ClearMoviesStickyError ();

	// initialize pointers to zero
	capture->myMovie  = 0;
	capture->myGWorld = nil;

	// initialize numbers with invalid values
	capture->next_frame_time   = -1;
	capture->next_frame_number = -1;
	capture->number_of_frames  = -1;
	capture->movie_start_time  = -1;
	capture->size              = cvSize (-1, -1);


	// we would use CFStringCreateWithFileSystemRepresentation (kCFAllocatorDefault, filename) on Mac OS X 10.4
	CFStringRef   inPath = CFStringCreateWithCString (kCFAllocatorDefault, filename, kCFStringEncodingISOLatin1);
	OPENCV_ASSERT ((inPath != nil), "icvOpenFile_QT_Movie", "couldnt create CFString from a string");

	// create the data reference
	myErr = QTNewDataReferenceFromFullPathCFString (inPath, kQTPOSIXPathStyle, 0, & myDataRef, & myDataRefType);
	if (myErr != noErr) {
		fprintf (stderr, "Couldn't create QTNewDataReferenceFromFullPathCFString().\n");
		return 0;
	}

	// get the Movie
	myErr = NewMovieFromDataRef(& capture->myMovie, newMovieActive | newMovieAsyncOK /* | newMovieIdleImportOK */,
								& myResID, myDataRef, myDataRefType);

	// dispose of the data reference handle - we no longer need it
	DisposeHandle (myDataRef);

	// if NewMovieFromDataRef failed, we already disposed the DataRef, so just return with an error
	if (myErr != noErr) {
		fprintf (stderr, "Couldn't create a NewMovieFromDataRef() - error is %d.\n",  myErr);
		return 0;
	}

	// count the number of video 'frames' in the movie by stepping through all of the
	// video 'interesting times', or in other words, the places where the movie displays
	// a new video sample. The time between these interesting times is not necessarily constant.
	{
		OSType      whichMediaType = VisualMediaCharacteristic;
		TimeValue   theTime        = -1;

		// find out movie start time
		GetMovieNextInterestingTime (capture->myMovie, short (nextTimeMediaSample + nextTimeEdgeOK),
									 1, & whichMediaType, TimeValue (0), 0, & theTime, NULL);
		if (theTime == -1) {
			fprintf (stderr, "Couldn't inquire first frame time\n");
			return 0;
		}
		capture->movie_start_time  = theTime;
		capture->next_frame_time   = theTime;
		capture->next_frame_number = 0;

		// count all 'interesting times' of the movie
		capture->number_of_frames  = 0;
		while (theTime >= 0) {
			GetMovieNextInterestingTime (capture->myMovie, short (nextTimeMediaSample),
										 1, & whichMediaType, theTime, 0, & theTime, NULL);
			capture->number_of_frames++;
		}
	}

	// get the bounding rectangle of the movie
	GetMoviesError ();
	GetMovieBox (capture->myMovie, & myRect);
	capture->size = cvSize (myRect.right - myRect.left, myRect.bottom - myRect.top);

	// create gworld for decompressed image
	myErr = QTNewGWorld (& capture->myGWorld, k32ARGBPixelFormat /* k24BGRPixelFormat geht leider nicht */,
						 & myRect, nil, nil, 0);
	OPENCV_ASSERT (myErr == noErr, "icvOpenFile_QT_Movie", "couldnt create QTNewGWorld() for output image");
	SetMovieGWorld (capture->myMovie, capture->myGWorld, nil);

	// build IplImage header that will point to the PixMap of the Movie's GWorld later on
	capture->image_rgb = cvCreateImageHeader (capture->size, IPL_DEPTH_8U, 4);

	// create IplImage that hold correctly formatted result
	capture->image_bgr = cvCreateImage (capture->size, IPL_DEPTH_8U, 3);

	// okay, that's it - should we wait until the Movie is playable?
	return 1;
}

/**
 * dispose of QuickTime Movie and free memory buffers
 *
 * @author Mark Asbach <asbach@ient.rwth-aachen.de>
 * @date   2005-11-04
 */
static int icvClose_QT_Movie (CvCapture_QT_Movie* capture) {
	OPENCV_ASSERT (capture,          "icvClose_QT_Movie", "'capture' is a NULL-pointer");

	// deallocate and free resources
	if (capture->myMovie) {
		cvReleaseImage       (& capture->image_bgr);
		cvReleaseImageHeader (& capture->image_rgb);
		DisposeGWorld        (capture->myGWorld);
		DisposeMovie         (capture->myMovie);
	}

	// okay, that's it
	return 1;
}

/**
 * get a capture property
 *
 * @author Mark Asbach <asbach@ient.rwth-aachen.de>
 * @date   2005-11-05
 */
static double icvGetProperty_QT_Movie (CvCapture_QT_Movie* capture, int property_id) {
	OPENCV_ASSERT (capture,                        "icvGetProperty_QT_Movie", "'capture' is a NULL-pointer");
	OPENCV_ASSERT (capture->myMovie,               "icvGetProperty_QT_Movie", "invalid Movie handle");
	OPENCV_ASSERT (capture->number_of_frames >  0, "icvGetProperty_QT_Movie", "movie has invalid number of frames");
	OPENCV_ASSERT (capture->movie_start_time >= 0, "icvGetProperty_QT_Movie", "movie has invalid start time");

	// inquire desired property
	switch (property_id) {
	case CV_CAP_PROP_POS_FRAMES:
		return (capture->next_frame_number);

	case CV_CAP_PROP_POS_MSEC:
	case CV_CAP_PROP_POS_AVI_RATIO: {
		TimeValue   position  = capture->next_frame_time - capture->movie_start_time;

		if (property_id == CV_CAP_PROP_POS_MSEC) {
			TimeScale   timescale = GetMovieTimeScale (capture->myMovie);
			return (static_cast<double> (position) * 1000.0 / timescale);
		} else {
			TimeValue   duration  = GetMovieDuration  (capture->myMovie);
			return (static_cast<double> (position) / duration);
		}
	}
	break; // never reached

	case CV_CAP_PROP_FRAME_WIDTH:
		return static_cast<double> (capture->size.width);

	case CV_CAP_PROP_FRAME_HEIGHT:
		return static_cast<double> (capture->size.height);

	case CV_CAP_PROP_FPS: {
		TimeValue   duration  = GetMovieDuration  (capture->myMovie);
		TimeScale   timescale = GetMovieTimeScale (capture->myMovie);

		return (capture->number_of_frames / (static_cast<double> (duration) / timescale));
	}

	case CV_CAP_PROP_FRAME_COUNT:
		return static_cast<double> (capture->number_of_frames);

	case CV_CAP_PROP_FOURCC:  // not implemented
	case CV_CAP_PROP_FORMAT:  // not implemented
	case CV_CAP_PROP_MODE:    // not implemented
	default:
		// unhandled or unknown capture property
		OPENCV_ERROR (CV_StsBadArg, "icvSetProperty_QT_Movie", "unknown or unhandled property_id");
		return CV_StsBadArg;
	}

	return 0;
}

/**
 * set a capture property. With movie files, it is only possible to set the
 * position (i.e. jump to a given time or frame number)
 *
 * @author Mark Asbach <asbach@ient.rwth-aachen.de>
 * @date   2005-11-05
 */
static int icvSetProperty_QT_Movie (CvCapture_QT_Movie* capture, int property_id, double value) {
	OPENCV_ASSERT (capture,                        "icvSetProperty_QT_Movie", "'capture' is a NULL-pointer");
	OPENCV_ASSERT (capture->myMovie,               "icvSetProperty_QT_Movie", "invalid Movie handle");
	OPENCV_ASSERT (capture->number_of_frames >  0, "icvSetProperty_QT_Movie", "movie has invalid number of frames");
	OPENCV_ASSERT (capture->movie_start_time >= 0, "icvSetProperty_QT_Movie", "movie has invalid start time");

	// inquire desired property
	//
	// rework these three points to really work through 'interesting times'.
	// with the current implementation, they result in wrong times or wrong frame numbers with content that
	// features varying frame durations
	switch (property_id) {
	case CV_CAP_PROP_POS_MSEC:
	case CV_CAP_PROP_POS_AVI_RATIO: {
		TimeValue    destination;
		OSType       myType     = VisualMediaCharacteristic;
		OSErr        myErr      = noErr;

		if (property_id == CV_CAP_PROP_POS_MSEC) {
			TimeScale  timescale   = GetMovieTimeScale      (capture->myMovie);
			destination = static_cast<TimeValue> (value / 1000.0 * timescale + capture->movie_start_time);
		} else {
			TimeValue  duration    = GetMovieDuration       (capture->myMovie);
			destination = static_cast<TimeValue> (value * duration + capture->movie_start_time);
		}

		// really seek?
		if (capture->next_frame_time == destination) {
			break;
		}

		// seek into which direction?
		if (capture->next_frame_time < destination) {
			while (capture->next_frame_time < destination) {
				capture->next_frame_number++;
				GetMovieNextInterestingTime (capture->myMovie, nextTimeStep, 1, & myType, capture->next_frame_time,
											 1, & capture->next_frame_time, NULL);
				myErr = GetMoviesError();
				if (myErr != noErr) {
					fprintf (stderr, "Couldn't go on to GetMovieNextInterestingTime() in icvGrabFrame_QT.\n");
					return 0;
				}
			}
		} else {
			while (capture->next_frame_time > destination) {
				capture->next_frame_number--;
				GetMovieNextInterestingTime (capture->myMovie, nextTimeStep, 1, & myType, capture->next_frame_time,
											 -1, & capture->next_frame_time, NULL);
				myErr = GetMoviesError();
				if (myErr != noErr) {
					fprintf (stderr, "Couldn't go back to GetMovieNextInterestingTime() in icvGrabFrame_QT.\n");
					return 0;
				}
			}
		}
	}
	break;

	case CV_CAP_PROP_POS_FRAMES: {
		TimeValue    destination = static_cast<TimeValue> (value);
		short        direction   = (destination > capture->next_frame_number) ? 1 : -1;
		OSType       myType      = VisualMediaCharacteristic;
		OSErr        myErr       = noErr;

		while (destination != capture->next_frame_number) {
			capture->next_frame_number += direction;
			GetMovieNextInterestingTime (capture->myMovie, nextTimeStep, 1, & myType, capture->next_frame_time,
										 direction, & capture->next_frame_time, NULL);
			myErr = GetMoviesError();
			if (myErr != noErr) {
				fprintf (stderr, "Couldn't step to desired frame number in icvGrabFrame_QT.\n");
				return 0;
			}
		}
	}
	break;

	default:
		// unhandled or unknown capture property
		OPENCV_ERROR (CV_StsBadArg, "icvSetProperty_QT_Movie", "unknown or unhandled property_id");
		return 0;
	}

	// positive result means success
	return 1;
}

/**
 * the original meaning of this method is to acquire raw frame data for the next video
 * frame but not decompress it. With the QuickTime video reader, this is reduced to
 * advance to the current frame time.
 *
 * @author Mark Asbach <asbach@ient.rwth-aachen.de>
 * @date   2005-11-06
 */
static int icvGrabFrame_QT_Movie (CvCapture_QT_Movie* capture) {
	OPENCV_ASSERT (capture,          "icvGrabFrame_QT_Movie", "'capture' is a NULL-pointer");
	OPENCV_ASSERT (capture->myMovie, "icvGrabFrame_QT_Movie", "invalid Movie handle");

	TimeValue    myCurrTime;
	OSType       myType     = VisualMediaCharacteristic;
	OSErr        myErr      = noErr;


	// jump to current video sample
	SetMovieTimeValue (capture->myMovie, capture->next_frame_time);
	myErr = GetMoviesError();
	if (myErr != noErr) {
		fprintf (stderr, "Couldn't SetMovieTimeValue() in icvGrabFrame_QT_Movie.\n");
		return  0;
	}

	// where are we now?
	myCurrTime = GetMovieTime (capture->myMovie, NULL);

	// increment counters
	capture->next_frame_number++;
	GetMovieNextInterestingTime (capture->myMovie, nextTimeStep, 1, & myType, myCurrTime, 1, & capture->next_frame_time, NULL);
	myErr = GetMoviesError();
	if (myErr != noErr) {
		fprintf (stderr, "Couldn't GetMovieNextInterestingTime() in icvGrabFrame_QT_Movie.\n");
		return 0;
	}

	// that's it
	return 1;
}

/**
 * render the current frame into an image buffer and convert to OpenCV IplImage
 * buffer layout (BGR sampling)
 *
 * @author Mark Asbach <asbach@ient.rwth-aachen.de>
 * @date   2005-11-06
 */
static const void* icvRetrieveFrame_QT_Movie (CvCapture_QT_Movie* capture, int) {
	OPENCV_ASSERT (capture,            "icvRetrieveFrame_QT_Movie", "'capture' is a NULL-pointer");
	OPENCV_ASSERT (capture->myMovie,   "icvRetrieveFrame_QT_Movie", "invalid Movie handle");
	OPENCV_ASSERT (capture->image_rgb, "icvRetrieveFrame_QT_Movie", "invalid source image");
	OPENCV_ASSERT (capture->image_bgr, "icvRetrieveFrame_QT_Movie", "invalid destination image");

	PixMapHandle  myPixMapHandle = nil;
	OSErr         myErr          = noErr;


	// invalidates the movie's display state so that the Movie Toolbox
	// redraws the movie the next time we call MoviesTask
	UpdateMovie (capture->myMovie);
	myErr = GetMoviesError ();
	if (myErr != noErr) {
		fprintf (stderr, "Couldn't UpdateMovie() in icvRetrieveFrame_QT_Movie().\n");
		return 0;
	}

	// service active movie (= redraw immediately)
	MoviesTask (capture->myMovie, 0L);
	myErr = GetMoviesError ();
	if (myErr != noErr) {
		fprintf (stderr, "MoviesTask() didn't succeed in icvRetrieveFrame_QT_Movie().\n");
		return 0;
	}

	// update IplImage header that points to PixMap of the Movie's GWorld.
	// unfortunately, cvCvtColor doesn't know ARGB, the QuickTime pixel format,
	// so we pass a modfied address.
	// ATTENTION: don't access the last pixel's alpha entry, it's inexistant
	myPixMapHandle = GetGWorldPixMap (capture->myGWorld);
	LockPixels (myPixMapHandle);
	cvSetData (capture->image_rgb, GetPixBaseAddr (myPixMapHandle) + 1, GetPixRowBytes (myPixMapHandle));

	// covert RGB of GWorld to BGR
	cvCvtColor (capture->image_rgb, capture->image_bgr, CV_RGBA2BGR);

	// allow QuickTime to access the buffer again
	UnlockPixels (myPixMapHandle);

	// always return the same image pointer
	return capture->image_bgr;
}


// ----------------------------------------------------------------------------------------
#pragma mark -
#pragma mark Capturing from Video Cameras

#ifdef USE_VDIG_VERSION

/// SequenceGrabber state structure for QuickTime
typedef struct CvCapture_QT_Cam_vdig {
	ComponentInstance  grabber;
	short              channel;
	GWorldPtr          myGWorld;
	PixMapHandle       pixmap;

	CvSize             size;
	long               number_of_frames;

	IplImage*          image_rgb; // will point to the PixMap of myGWorld
	IplImage*          image_bgr; // will be returned by icvRetrieveFrame_QT()

} CvCapture_QT_Cam;

#else

typedef struct CvCapture_QT_Cam_barg {
	SeqGrabComponent   grabber;
	SGChannel          channel;
	GWorldPtr          gworld;
	Rect               bounds;
	ImageSequence      sequence;

	volatile bool      got_frame;

	CvSize             size;
	IplImage*          image_rgb; // will point to the PixMap of myGWorld
	IplImage*          image_bgr; // will be returned by icvRetrieveFrame_QT()

} CvCapture_QT_Cam;

#endif

static       int         icvOpenCamera_QT        (CvCapture_QT_Cam* capture, const int index);
static       int         icvClose_QT_Cam         (CvCapture_QT_Cam* capture);
static       double      icvGetProperty_QT_Cam   (CvCapture_QT_Cam* capture, int property_id);
static       int         icvSetProperty_QT_Cam   (CvCapture_QT_Cam* capture, int property_id, double value);
static       int         icvGrabFrame_QT_Cam     (CvCapture_QT_Cam* capture);
static const void*       icvRetrieveFrame_QT_Cam (CvCapture_QT_Cam* capture, int);


/**
 * Initialize memory structure and call method to open camera
 *
 * @author Mark Asbach <asbach@ient.rwth-aachen.de>
 * @date 2006-01-29
 */
static CvCapture_QT_Cam* icvCaptureFromCam_QT (const int index) {
	if (! did_enter_movies) {
		EnterMovies();
		did_enter_movies = 1;
	}

	CvCapture_QT_Cam* capture = 0;

	if (index >= 0) {
		capture = (CvCapture_QT_Cam*) cvAlloc (sizeof (*capture));
		memset (capture, 0, sizeof(*capture));

		if (!icvOpenCamera_QT (capture, index)) {
			cvFree (&capture);
		}
	}

	return capture;
}

/// capture properties currently unimplemented for QuickTime camera interface
static double icvGetProperty_QT_Cam (CvCapture_QT_Cam* capture, int property_id) {
	assert (0);
	return 0;
}

/// capture properties currently unimplemented for QuickTime camera interface
static int icvSetProperty_QT_Cam (CvCapture_QT_Cam* capture, int property_id, double value) {
	assert (0);
	return 0;
}

#ifdef USE_VDIG_VERSION
#pragma mark Capturing using VDIG

/**
 * Open a quicktime video grabber component. This could be an attached
 * IEEE1394 camera, a web cam, an iSight or digitizer card / video converter.
 *
 * @author Mark Asbach <asbach@ient.rwth-aachen.de>
 * @date 2006-01-29
 */
static int icvOpenCamera_QT (CvCapture_QT_Cam* capture, const int index) {
	OPENCV_ASSERT (capture,            "icvOpenCamera_QT", "'capture' is a NULL-pointer");
	OPENCV_ASSERT (index >= 0, "icvOpenCamera_QT", "camera index is negative");

	ComponentDescription	component_description;
	Component				component = 0;
	int                     number_of_inputs = 0;
	Rect                    myRect;
	ComponentResult			result = noErr;


	// travers all components and count video digitizer channels
	component_description.componentType         = videoDigitizerComponentType;
	component_description.componentSubType      = 0L;
	component_description.componentManufacturer = 0L;
	component_description.componentFlags        = 0L;
	component_description.componentFlagsMask    = 0L;
	do {
		// traverse component list
		component = FindNextComponent (component, & component_description);

		// found a component?
		if (component) {
			// dump component name
#ifndef NDEBUG
			ComponentDescription  desc;
			Handle                nameHandle = NewHandleClear (200);
			char                  nameBuffer [255];

			result = GetComponentInfo (component, & desc, nameHandle, nil, nil);
			OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt GetComponentInfo()");
			OPENCV_ASSERT (*nameHandle, "icvOpenCamera_QT", "No name returned by GetComponentInfo()");
			snprintf (nameBuffer, (**nameHandle) + 1, "%s", (char*) (* nameHandle + 1));
			printf ("- Videodevice: %s\n", nameBuffer);
			DisposeHandle (nameHandle);
#endif

			// open component to count number of inputs
			capture->grabber = OpenComponent (component);
			if (capture->grabber) {
				result = VDGetNumberOfInputs (capture->grabber, & capture->channel);
				if (result != noErr) {
					fprintf (stderr, "Couldnt GetNumberOfInputs: %d\n", (int) result);
				} else {
#ifndef NDEBUG
					printf ("  Number of inputs: %d\n", (int) capture->channel + 1);
#endif

					// add to overall number of inputs
					number_of_inputs += capture->channel + 1;

					// did the user select an input that falls into this device's
					// range of inputs? Then leave the loop
					if (number_of_inputs > index) {
						// calculate relative channel index
						capture->channel = index - number_of_inputs + capture->channel + 1;
						OPENCV_ASSERT (capture->channel >= 0, "icvOpenCamera_QT", "negative channel number");

						// dump channel name
#ifndef NDEBUG
						char  name[256];
						Str255  nameBuffer;

						result = VDGetInputName (capture->grabber, capture->channel, nameBuffer);
						OPENCV_ASSERT (result == noErr, "ictOpenCamera_QT", "couldnt GetInputName()");
						snprintf (name, *nameBuffer, "%s", (char*) (nameBuffer + 1));
						printf ("  Choosing input %d - %s\n", (int) capture->channel, name);
#endif

						// leave the loop
						break;
					}
				}

				// obviously no inputs of this device/component were needed
				CloseComponent (capture->grabber);
			}
		}
	} while (component);

	// did we find the desired input?
	if (! component) {
		fprintf(stderr, "Not enough inputs available - can't choose input %d\n", index);
		return 0;
	}

	// -- Okay now, we selected the digitizer input, lets set up digitizer destination --

	ClearMoviesStickyError();

	// Select the desired input
	result = VDSetInput (capture->grabber, capture->channel);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt select video digitizer input");

	// get the bounding rectangle of the video digitizer
	result = VDGetActiveSrcRect (capture->grabber, capture->channel, & myRect);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt create VDGetActiveSrcRect from digitizer");
	myRect.right = 640; myRect.bottom = 480;
	capture->size = cvSize (myRect.right - myRect.left, myRect.bottom - myRect.top);
	printf ("Source rect is %d, %d -- %d, %d\n", (int) myRect.left, (int) myRect.top, (int) myRect.right, (int) myRect.bottom);

	// create offscreen GWorld
	result = QTNewGWorld (& capture->myGWorld, k32ARGBPixelFormat, & myRect, nil, nil, 0);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt create QTNewGWorld() for output image");

	// get pixmap
	capture->pixmap = GetGWorldPixMap (capture->myGWorld);
	result = GetMoviesError ();
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt get pixmap");

	// set digitizer rect
	result = VDSetDigitizerRect (capture->grabber, & myRect);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt create VDGetActiveSrcRect from digitizer");

	// set destination of digitized input
	result = VDSetPlayThruDestination (capture->grabber, capture->pixmap, & myRect, nil, nil);
	printf ("QuickTime error: %d\n", (int) result);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set video destination");

	// get destination of digitized images
	result = VDGetPlayThruDestination (capture->grabber, & capture->pixmap, nil, nil, nil);
	printf ("QuickTime error: %d\n", (int) result);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt get video destination");
	OPENCV_ASSERT (capture->pixmap != nil, "icvOpenCamera_QT", "empty set video destination");

	// get the bounding rectangle of the video digitizer
	GetPixBounds (capture->pixmap, & myRect);
	capture->size = cvSize (myRect.right - myRect.left, myRect.bottom - myRect.top);

	// build IplImage header that will point to the PixMap of the Movie's GWorld later on
	capture->image_rgb = cvCreateImageHeader (capture->size, IPL_DEPTH_8U, 4);
	OPENCV_ASSERT (capture->image_rgb, "icvOpenCamera_QT", "couldnt create image header");

	// create IplImage that hold correctly formatted result
	capture->image_bgr = cvCreateImage (capture->size, IPL_DEPTH_8U, 3);
	OPENCV_ASSERT (capture->image_bgr, "icvOpenCamera_QT", "couldnt create image");

	// notify digitizer component, that we well be starting grabbing soon
	result = VDCaptureStateChanging (capture->grabber, vdFlagCaptureIsForRecord | vdFlagCaptureStarting | vdFlagCaptureLowLatency);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set capture state");


	// yeah, we did it
	return 1;
}

static int icvClose_QT_Cam (CvCapture_QT_Cam* capture) {
	OPENCV_ASSERT (capture, "icvClose_QT_Cam", "'capture' is a NULL-pointer");

	ComponentResult	result = noErr;

	// notify digitizer component, that we well be stopping grabbing soon
	result = VDCaptureStateChanging (capture->grabber, vdFlagCaptureStopping);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set capture state");

	// release memory
	cvReleaseImage       (& capture->image_bgr);
	cvReleaseImageHeader (& capture->image_rgb);
	DisposeGWorld        (capture->myGWorld);
	CloseComponent       (capture->grabber);

	// sucessful
	return 1;
}

static int icvGrabFrame_QT_Cam (CvCapture_QT_Cam* capture) {
	OPENCV_ASSERT (capture,          "icvGrabFrame_QT_Cam", "'capture' is a NULL-pointer");
	OPENCV_ASSERT (capture->grabber, "icvGrabFrame_QT_Cam", "'grabber' is a NULL-pointer");

	ComponentResult	result = noErr;

	// grab one frame
	result = VDGrabOneFrame (capture->grabber);
	if (result != noErr) {
		fprintf (stderr, "VDGrabOneFrame failed\n");
		return 0;
	}

	// successful
	return 1;
}

static const void* icvRetrieveFrame_QT_Cam (CvCapture_QT_Cam* capture, int) {
	OPENCV_ASSERT (capture, "icvRetrieveFrame_QT_Cam", "'capture' is a NULL-pointer");

	PixMapHandle  myPixMapHandle = nil;

	// update IplImage header that points to PixMap of the Movie's GWorld.
	// unfortunately, cvCvtColor doesn't know ARGB, the QuickTime pixel format,
	// so we pass a modfied address.
	// ATTENTION: don't access the last pixel's alpha entry, it's inexistant
	//myPixMapHandle = GetGWorldPixMap (capture->myGWorld);
	myPixMapHandle = capture->pixmap;
	LockPixels (myPixMapHandle);
	cvSetData (capture->image_rgb, GetPixBaseAddr (myPixMapHandle) + 1, GetPixRowBytes (myPixMapHandle));

	// covert RGB of GWorld to BGR
	cvCvtColor (capture->image_rgb, capture->image_bgr, CV_RGBA2BGR);

	// allow QuickTime to access the buffer again
	UnlockPixels (myPixMapHandle);

	// always return the same image pointer
	return capture->image_bgr;
}

#else
#pragma mark Capturing using Sequence Grabber

static OSErr icvDataProc_QT_Cam (SGChannel channel, Ptr raw_data, long len, long*, long, TimeValue, short, long refCon) {
	CvCapture_QT_Cam*   capture = (CvCapture_QT_Cam*) refCon;
	CodecFlags          ignore;
	ComponentResult     err     = noErr;


	// we need valid pointers
	OPENCV_ASSERT (capture,          "icvDataProc_QT_Cam", "'capture' is a NULL-pointer");
	OPENCV_ASSERT (capture->gworld,  "icvDataProc_QT_Cam", "'gworld' is a NULL-pointer");
	OPENCV_ASSERT (raw_data,         "icvDataProc_QT_Cam", "'raw_data' is a NULL-pointer");

	// create a decompression sequence the first time
	if (capture->sequence == 0) {
		ImageDescriptionHandle   description = (ImageDescriptionHandle) NewHandle(0);

		// we need a decompression sequence that fits the raw data coming from the camera
		err = SGGetChannelSampleDescription (channel, (Handle) description);
		OPENCV_ASSERT (err == noErr, "icvDataProc_QT_Cam", "couldnt get channel sample description");

		//*************************************************************************************//
		//This fixed a bug when Quicktime is called twice to grab a frame (black band bug) - Yannick Verdie 2010
		Rect sourceRect;
		sourceRect.top = 0;
		sourceRect.left = 0;
		sourceRect.right = (**description).width;
		sourceRect.bottom = (**description).height;

		MatrixRecord scaleMatrix;
		RectMatrix(&scaleMatrix, &sourceRect, &capture->bounds);

		err = DecompressSequenceBegin (&capture->sequence, description, capture->gworld, 0, &capture->bounds, &scaleMatrix, srcCopy, NULL, 0, codecNormalQuality, bestSpeedCodec);
		//**************************************************************************************//

		OPENCV_ASSERT (err == noErr, "icvDataProc_QT_Cam", "couldnt begin decompression sequence");
		DisposeHandle ((Handle) description);
	}

	// okay, we have a decompression sequence -> decompress!
	err = DecompressSequenceFrameS (capture->sequence, raw_data, len, 0, &ignore, nil);
	if (err != noErr) {
		fprintf (stderr, "icvDataProc_QT_Cam: couldn't decompress frame - %d\n", (int) err);
		return err;
	}

	// check if we dropped a frame
	/*#ifndef NDEBUG
		if (capture->got_frame)
			fprintf (stderr, "icvDataProc_QT_Cam: frame was dropped\n");
#endif*/

// everything worked as expected
capture->got_frame = true;
return noErr;
}


static int icvOpenCamera_QT (CvCapture_QT_Cam* capture, const int index) {
	OPENCV_ASSERT (capture,    "icvOpenCamera_QT", "'capture' is a NULL-pointer");
	OPENCV_ASSERT (index >= 0, "icvOpenCamera_QT", "camera index is negative");

	PixMapHandle  pixmap       = nil;
	OSErr         result       = noErr;

	// open sequence grabber component
	capture->grabber = OpenDefaultComponent (SeqGrabComponentType, 0);
	OPENCV_ASSERT (capture->grabber, "icvOpenCamera_QT", "couldnt create image");

	// initialize sequence grabber component
	result = SGInitialize (capture->grabber);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt initialize sequence grabber");
	result = SGSetDataRef (capture->grabber, 0, 0, seqGrabDontMakeMovie);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set data reference of sequence grabber");

	// set up video channel
	result = SGNewChannel (capture->grabber, VideoMediaType, & (capture->channel));
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt create new video channel");

	// select the camera indicated by index
	SGDeviceList device_list = 0;
	result = SGGetChannelDeviceList (capture->channel, 0, & device_list);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt get channel device list");
	for (int i = 0, current_index = 1; i < (*device_list)->count; i++) {
		SGDeviceName device = (*device_list)->entry[i];
		if (device.flags == 0) {
			if (current_index == index) {
				result = SGSetChannelDevice (capture->channel, device.name);
				OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set the channel video device");
				break;
			}
			current_index++;
		}
	}
	result = SGDisposeDeviceList (capture->grabber, device_list);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt dispose the channel device list");

	// query natural camera resolution -- this will be wrong, but will be an upper
	// bound on the actual resolution -- the actual resolution is set below
	// after starting the frame grabber
	result = SGGetSrcVideoBounds (capture->channel, & (capture->bounds));
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set video channel bounds");

	// create offscreen GWorld
	result = QTNewGWorld (& (capture->gworld), k32ARGBPixelFormat, & (capture->bounds), 0, 0, 0);
	result = SGSetGWorld (capture->grabber, capture->gworld, 0);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set GWorld for sequence grabber");
	result = SGSetChannelBounds (capture->channel, & (capture->bounds));
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set video channel bounds");
	result = SGSetChannelUsage (capture->channel, seqGrabRecord);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set channel usage");

	// start recording so we can size
	result = SGStartRecord (capture->grabber);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt start recording");

	// don't know *actual* resolution until now
	ImageDescriptionHandle imageDesc = (ImageDescriptionHandle)NewHandle(0);
	result = SGGetChannelSampleDescription(capture->channel, (Handle)imageDesc);
	OPENCV_ASSERT( result == noErr, "icvOpenCamera_QT", "couldn't get image size");
	capture->bounds.right = (**imageDesc).width;
	capture->bounds.bottom = (**imageDesc).height;
	DisposeHandle ((Handle) imageDesc);

	// stop grabber so that we can reset the parameters to the right size
	result = SGStop (capture->grabber);
	OPENCV_ASSERT (result == noErr, "icveClose_QT_Cam", "couldnt stop recording");

	// reset GWorld to correct image size
	GWorldPtr tmpgworld;
	result = QTNewGWorld( &tmpgworld, k32ARGBPixelFormat, &(capture->bounds), 0, 0, 0);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt create offscreen GWorld");
	result = SGSetGWorld( capture->grabber, tmpgworld, 0);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set GWorld for sequence grabber");
	DisposeGWorld( capture->gworld );
	capture->gworld = tmpgworld;

	result = SGSetChannelBounds (capture->channel, & (capture->bounds));
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set video channel bounds");

	// allocate images
	capture->size = cvSize (capture->bounds.right - capture->bounds.left, capture->bounds.bottom - capture->bounds.top);

	// build IplImage header that points to the PixMap of the Movie's GWorld.
	// unfortunately, cvCvtColor doesn't know ARGB, the QuickTime pixel format,
	// so we shift the base address by one byte.
	// ATTENTION: don't access the last pixel's alpha entry, it's inexistant
	capture->image_rgb = cvCreateImageHeader (capture->size, IPL_DEPTH_8U, 4);
	OPENCV_ASSERT (capture->image_rgb, "icvOpenCamera_QT", "couldnt create image header");
	pixmap = GetGWorldPixMap (capture->gworld);
	OPENCV_ASSERT (pixmap, "icvOpenCamera_QT", "didn't get GWorld PixMap handle");
	LockPixels (pixmap);
	cvSetData (capture->image_rgb, GetPixBaseAddr (pixmap) + 1, GetPixRowBytes (pixmap));

	// create IplImage that hold correctly formatted result
	capture->image_bgr = cvCreateImage (capture->size, IPL_DEPTH_8U, 3);
	OPENCV_ASSERT (capture->image_bgr, "icvOpenCamera_QT", "couldnt create image");


	// tell the sequence grabber to invoke our data proc
	result = SGSetDataProc (capture->grabber, NewSGDataUPP (icvDataProc_QT_Cam), (long) capture);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set data proc");

	// start recording
	result = SGStartRecord (capture->grabber);
	OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt start recording");

	return 1;
}


static int icvClose_QT_Cam (CvCapture_QT_Cam* capture) {
	OPENCV_ASSERT (capture, "icvClose_QT_Cam", "'capture' is a NULL-pointer");

	OSErr  result = noErr;


	// stop recording
	result = SGStop (capture->grabber);
	OPENCV_ASSERT (result == noErr, "icveClose_QT_Cam", "couldnt stop recording");

	// close sequence grabber component
	result = CloseComponent (capture->grabber);
	OPENCV_ASSERT (result == noErr, "icveClose_QT_Cam", "couldnt close sequence grabber component");

	// end decompression sequence
	CDSequenceEnd (capture->sequence);

	// free memory
	cvReleaseImage (& capture->image_bgr);
	cvReleaseImageHeader (& capture->image_rgb);
	DisposeGWorld (capture->gworld);

	// sucessful
	return 1;
}

static int icvGrabFrame_QT_Cam (CvCapture_QT_Cam* capture) {
	OPENCV_ASSERT (capture,          "icvGrabFrame_QT_Cam", "'capture' is a NULL-pointer");
	OPENCV_ASSERT (capture->grabber, "icvGrabFrame_QT_Cam", "'grabber' is a NULL-pointer");

	ComponentResult	result = noErr;


	// grab one frame
	result = SGIdle (capture->grabber);
	if (result != noErr) {
		fprintf (stderr, "SGIdle failed in icvGrabFrame_QT_Cam with error %d\n", (int) result);
		return 0;
	}

	// successful
	return 1;
}

static const void* icvRetrieveFrame_QT_Cam (CvCapture_QT_Cam* capture, int) {
	OPENCV_ASSERT (capture,            "icvRetrieveFrame_QT_Cam", "'capture' is a NULL-pointer");
	OPENCV_ASSERT (capture->image_rgb, "icvRetrieveFrame_QT_Cam", "invalid source image");
	OPENCV_ASSERT (capture->image_bgr, "icvRetrieveFrame_QT_Cam", "invalid destination image");

	OSErr         myErr          = noErr;


	// service active sequence grabbers (= redraw immediately)
	while (! capture->got_frame) {
		myErr = SGIdle (capture->grabber);
		if (myErr != noErr) {
			fprintf (stderr, "SGIdle() didn't succeed in icvRetrieveFrame_QT_Cam().\n");
			return 0;
		}
	}

	// covert RGB of GWorld to BGR
	cvCvtColor (capture->image_rgb, capture->image_bgr, CV_RGBA2BGR);

	// reset grabbing status
	capture->got_frame = false;

	// always return the same image pointer
	return capture->image_bgr;
}

#endif


typedef struct CvVideoWriter_QT {

	DataHandler data_handler;
	Movie movie;
	Track track;
	Media video;

	ICMCompressionSessionRef compression_session_ref;

	TimeValue duration_per_sample;
} CvVideoWriter_QT;


static TimeScale const TIME_SCALE = 600;

									static OSStatus icvEncodedFrameOutputCallback(
										void* writer,
										ICMCompressionSessionRef compression_session_ref,
										OSStatus error,
										ICMEncodedFrameRef encoded_frame_ref,
										void* reserved
									);

									static void icvSourceTrackingCallback(
										void* source_tracking_ref_con,
										ICMSourceTrackingFlags source_tracking_flags,
										void* source_frame_ref_con,
										void* reserved
									);

									static int icvWriteFrame_QT(
										CvVideoWriter_QT* video_writer,
										const IplImage* image
) {
	CVPixelBufferRef pixel_buffer_ref = NULL;
	CVReturn retval =
		CVPixelBufferCreate(
			kCFAllocatorDefault,
			image->width, image->height, k24RGBPixelFormat,
			NULL /* pixel_buffer_attributes */,
			&pixel_buffer_ref
		);

	// convert BGR IPL image to RGB pixel buffer
	IplImage* image_rgb =
		cvCreateImageHeader(
			cvSize( image->width, image->height ),
			IPL_DEPTH_8U,
			3
		);

	retval = CVPixelBufferLockBaseAddress( pixel_buffer_ref, 0 );

	void* base_address = CVPixelBufferGetBaseAddress( pixel_buffer_ref );
	size_t bytes_per_row = CVPixelBufferGetBytesPerRow( pixel_buffer_ref );
	cvSetData( image_rgb, base_address, bytes_per_row );

	cvConvertImage( image, image_rgb, CV_CVTIMG_SWAP_RB );

	retval = CVPixelBufferUnlockBaseAddress( pixel_buffer_ref, 0 );

	cvReleaseImageHeader( &image_rgb );

	ICMSourceTrackingCallbackRecord source_tracking_callback_record;
	source_tracking_callback_record.sourceTrackingCallback =
		icvSourceTrackingCallback;
	source_tracking_callback_record.sourceTrackingRefCon = NULL;

	OSStatus status =
		ICMCompressionSessionEncodeFrame(
			video_writer->compression_session_ref,
			pixel_buffer_ref,
			0,
			video_writer->duration_per_sample,
			kICMValidTime_DisplayDurationIsValid,
			NULL,
			&source_tracking_callback_record,
			static_cast<void*>( &pixel_buffer_ref )
		);

	return 0;
}

static void icvReleaseVideoWriter_QT( CvVideoWriter_QT** writer ) {
	if ( ( writer != NULL ) && ( *writer != NULL ) ) {
		CvVideoWriter_QT* video_writer = *writer;

		// force compression session to complete encoding of outstanding source
		// frames
		ICMCompressionSessionCompleteFrames(
			video_writer->compression_session_ref, TRUE, 0, 0
		);

		EndMediaEdits( video_writer->video );

		ICMCompressionSessionRelease( video_writer->compression_session_ref );

		InsertMediaIntoTrack(
			video_writer->track,
			0,
			0,
			GetMediaDuration( video_writer->video ),
			FixRatio( 1, 1 )
		);

		UpdateMovieInStorage( video_writer->movie, video_writer->data_handler );

		CloseMovieStorage( video_writer->data_handler );

		/*
		        // export to AVI
		        Handle data_ref;
		        OSType data_ref_type;
		        QTNewDataReferenceFromFullPathCFString(
		            CFSTR( "/Users/seibert/Desktop/test.avi" ), kQTPOSIXPathStyle, 0,
		            &data_ref, &data_ref_type
		        );

		        ConvertMovieToDataRef( video_writer->movie, NULL, data_ref,
		            data_ref_type, kQTFileTypeAVI, 'TVOD', 0, NULL );

		        DisposeHandle( data_ref );
		*/

		DisposeMovie( video_writer->movie );

		cvFree( writer );
	}
}

static OSStatus icvEncodedFrameOutputCallback(
	void* writer,
	ICMCompressionSessionRef compression_session_ref,
	OSStatus error,
	ICMEncodedFrameRef encoded_frame_ref,
	void* reserved
) {
	CvVideoWriter_QT* video_writer = static_cast<CvVideoWriter_QT*>( writer );

	OSStatus err = AddMediaSampleFromEncodedFrame( video_writer->video,
				   encoded_frame_ref, NULL );

	return err;
}

static void icvSourceTrackingCallback(
	void* source_tracking_ref_con,
	ICMSourceTrackingFlags source_tracking_flags,
	void* source_frame_ref_con,
	void* reserved
) {
	if ( source_tracking_flags & kICMSourceTracking_ReleasedPixelBuffer ) {
		CVPixelBufferRelease(
			*static_cast<CVPixelBufferRef*>( source_frame_ref_con )
		);
	}
}


static CvVideoWriter_QT* icvCreateVideoWriter_QT(
	const char* filename,
	int fourcc,
	double fps,
	CvSize frame_size,
	int is_color
) {
	CV_FUNCNAME( "icvCreateVideoWriter" );

	CvVideoWriter_QT* video_writer =
		static_cast<CvVideoWriter_QT*>( cvAlloc( sizeof( CvVideoWriter_QT ) ) );
	memset( video_writer, 0, sizeof( CvVideoWriter_QT ) );

	Handle                            data_ref     = NULL;
	OSType                            data_ref_type;
	DataHandler                       data_handler = NULL;
	Movie                             movie        = NULL;
	ICMCompressionSessionOptionsRef   options_ref  = NULL;
	ICMCompressionSessionRef          compression_session_ref = NULL;
	CFStringRef                       out_path     = nil;
	Track                             video_track  = nil;
	Media                             video        = nil;
	OSErr                             err          = noErr;

	__BEGIN__

	// validate input arguments
	if ( filename == NULL ) {
		CV_ERROR( CV_StsBadArg, "Video file name must not be NULL" );
	}
	if ( fps <= 0.0 ) {
		CV_ERROR( CV_StsBadArg, "FPS must be larger than 0.0" );
	}
	if ( ( frame_size.width <= 0 ) || ( frame_size.height <= 0 ) ) {
		CV_ERROR( CV_StsBadArg,
				  "Frame width and height must be larger than 0" );
	}

	// initialize QuickTime
	if ( !did_enter_movies ) {
		err = EnterMovies();
		if ( err != noErr ) {
			CV_ERROR( CV_StsInternal, "Unable to initialize QuickTime" );
		}
		did_enter_movies = 1;
	}

	// convert the file name into a data reference
	out_path = CFStringCreateWithCString( kCFAllocatorDefault, filename, kCFStringEncodingISOLatin1 );
	CV_ASSERT( out_path != nil );
	err = QTNewDataReferenceFromFullPathCFString( out_path, kQTPOSIXPathStyle,
			0, &data_ref, &data_ref_type );
	CFRelease( out_path );
	if ( err != noErr ) {
		CV_ERROR( CV_StsInternal,
				  "Cannot create data reference from file name" );
	}

	// create a new movie on disk
	err = CreateMovieStorage( data_ref, data_ref_type, 'TVOD',
							  smCurrentScript, newMovieActive, &data_handler, &movie );

	if ( err != noErr ) {
		CV_ERROR( CV_StsInternal, "Cannot create movie storage" );
	}

	// create a track with video
	video_track = NewMovieTrack (movie,
								 FixRatio( frame_size.width, 1 ),
								 FixRatio( frame_size.height, 1 ),
								 kNoVolume);
	err = GetMoviesError();
	if ( err != noErr ) {
		CV_ERROR( CV_StsInternal, "Cannot create video track" );
	}
	video = NewTrackMedia( video_track, VideoMediaType, TIME_SCALE, nil, 0 );
	err = GetMoviesError();
	if ( err != noErr ) {
		CV_ERROR( CV_StsInternal, "Cannot create video media" );
	}

	CodecType codecType;
	switch ( fourcc ) {
	case CV_FOURCC( 'D', 'I', 'B', ' ' ):
		codecType = kRawCodecType;
		break;
	default:
		codecType = kRawCodecType;
		break;
	}

	// start a compression session
	err = ICMCompressionSessionOptionsCreate( kCFAllocatorDefault,
			&options_ref );
	if ( err != noErr ) {
		CV_ERROR( CV_StsInternal, "Cannot create compression session options" );
	}
	err = ICMCompressionSessionOptionsSetAllowTemporalCompression( options_ref,
			true );
	if ( err != noErr) {
		CV_ERROR( CV_StsInternal, "Cannot enable temporal compression" );
	}
	err = ICMCompressionSessionOptionsSetAllowFrameReordering( options_ref,
			true );
	if ( err != noErr) {
		CV_ERROR( CV_StsInternal, "Cannot enable frame reordering" );
	}

	ICMEncodedFrameOutputRecord encoded_frame_output_record;
	encoded_frame_output_record.encodedFrameOutputCallback =
		icvEncodedFrameOutputCallback;
	encoded_frame_output_record.encodedFrameOutputRefCon =
		static_cast<void*>( video_writer );
	encoded_frame_output_record.frameDataAllocator = NULL;

	err = ICMCompressionSessionCreate( kCFAllocatorDefault, frame_size.width,
									   frame_size.height, codecType, TIME_SCALE, options_ref,
									   NULL /*source_pixel_buffer_attributes*/, &encoded_frame_output_record,
									   &compression_session_ref );
	ICMCompressionSessionOptionsRelease( options_ref );
	if ( err != noErr ) {
		CV_ERROR( CV_StsInternal, "Cannot create compression session" );
	}

	err = BeginMediaEdits( video );
	if ( err != noErr ) {
		CV_ERROR( CV_StsInternal, "Cannot begin media edits" );
	}

	// fill in the video writer structure
	video_writer->data_handler = data_handler;
	video_writer->movie = movie;
	video_writer->track = video_track;
	video_writer->video = video;
	video_writer->compression_session_ref = compression_session_ref;
	video_writer->duration_per_sample =
		static_cast<TimeValue>( static_cast<double>( TIME_SCALE ) / fps );

	__END__

	// clean up in case of error (unless error processing mode is
	// CV_ErrModeLeaf)
	if ( err != noErr ) {
		if ( options_ref != NULL ) {
			ICMCompressionSessionOptionsRelease( options_ref );
		}
		if ( compression_session_ref != NULL ) {
			ICMCompressionSessionRelease( compression_session_ref );
		}
		if ( data_handler != NULL ) {
			CloseMovieStorage( data_handler );
		}
		if ( movie != NULL ) {
			DisposeMovie( movie );
		}
		if ( data_ref != NULL ) {
			DeleteMovieStorage( data_ref, data_ref_type );
			DisposeHandle( data_ref );
		}
		cvFree( reinterpret_cast<void**>( &video_writer ) );
		video_writer = NULL;
	}

	return video_writer;
}


/**
*
*   Wrappers for the new C++ CvCapture & CvVideoWriter structures
*
*/

class CvCapture_QT_Movie_CPP : public CvCapture {
public:
	CvCapture_QT_Movie_CPP() { captureQT = 0; }
	virtual ~CvCapture_QT_Movie_CPP() { close(); }

	virtual bool open( const char* filename );
	virtual void close();

	virtual double getProperty(int);
	virtual bool setProperty(int, double);
	virtual bool grabFrame();
	virtual IplImage* retrieveFrame(int);
	virtual int getCaptureDomain() { return CV_CAP_QT; } // Return the type of the capture object: CV_CAP_VFW, etc...
protected:

	CvCapture_QT_Movie* captureQT;
};

bool CvCapture_QT_Movie_CPP::open( const char* filename ) {
	close();
	captureQT = icvCaptureFromFile_QT( filename );
	return captureQT != 0;
}

void CvCapture_QT_Movie_CPP::close() {
	if ( captureQT ) {
		icvClose_QT_Movie( captureQT );
		cvFree( &captureQT );
	}
}

bool CvCapture_QT_Movie_CPP::grabFrame() {
	return captureQT ? icvGrabFrame_QT_Movie( captureQT ) != 0 : false;
}

IplImage* CvCapture_QT_Movie_CPP::retrieveFrame(int) {
	return captureQT ? (IplImage*)icvRetrieveFrame_QT_Movie( captureQT, 0 ) : 0;
}

double CvCapture_QT_Movie_CPP::getProperty( int propId ) {
	return captureQT ? icvGetProperty_QT_Movie( captureQT, propId ) : 0;
}

bool CvCapture_QT_Movie_CPP::setProperty( int propId, double value ) {
	return captureQT ? icvSetProperty_QT_Movie( captureQT, propId, value ) != 0 : false;
}

CvCapture* cvCreateFileCapture_QT( const char* filename ) {
	CvCapture_QT_Movie_CPP* capture = new CvCapture_QT_Movie_CPP;

	if ( capture->open( filename )) {
		return capture;
	}

	delete capture;
	return 0;
}


/////////////////////////////////////

class CvCapture_QT_Cam_CPP : public CvCapture {
public:
	CvCapture_QT_Cam_CPP() { captureQT = 0; }
	virtual ~CvCapture_QT_Cam_CPP() { close(); }

	virtual bool open( int index );
	virtual void close();

	virtual double getProperty(int);
	virtual bool setProperty(int, double);
	virtual bool grabFrame();
	virtual IplImage* retrieveFrame(int);
	virtual int getCaptureDomain() { return CV_CAP_QT; } // Return the type of the capture object: CV_CAP_VFW, etc...
protected:

	CvCapture_QT_Cam* captureQT;
};

bool CvCapture_QT_Cam_CPP::open( int index ) {
	close();
	captureQT = icvCaptureFromCam_QT( index );
	return captureQT != 0;
}

void CvCapture_QT_Cam_CPP::close() {
	if ( captureQT ) {
		icvClose_QT_Cam( captureQT );
		cvFree( &captureQT );
	}
}

bool CvCapture_QT_Cam_CPP::grabFrame() {
	return captureQT ? icvGrabFrame_QT_Cam( captureQT ) != 0 : false;
}

IplImage* CvCapture_QT_Cam_CPP::retrieveFrame(int) {
	return captureQT ? (IplImage*)icvRetrieveFrame_QT_Cam( captureQT, 0 ) : 0;
}

double CvCapture_QT_Cam_CPP::getProperty( int propId ) {
	return captureQT ? icvGetProperty_QT_Cam( captureQT, propId ) : 0;
}

bool CvCapture_QT_Cam_CPP::setProperty( int propId, double value ) {
	return captureQT ? icvSetProperty_QT_Cam( captureQT, propId, value ) != 0 : false;
}

CvCapture* cvCreateCameraCapture_QT( int index ) {
	CvCapture_QT_Cam_CPP* capture = new CvCapture_QT_Cam_CPP;

	if ( capture->open( index )) {
		return capture;
	}

	delete capture;
	return 0;
}

/////////////////////////////////

class CvVideoWriter_QT_CPP : public CvVideoWriter {
public:
	CvVideoWriter_QT_CPP() { writerQT = 0; }
	virtual ~CvVideoWriter_QT_CPP() { close(); }

	virtual bool open( const char* filename, int fourcc,
					   double fps, CvSize frameSize, bool isColor );
	virtual void close();
	virtual bool writeFrame( const IplImage* );

protected:
	CvVideoWriter_QT* writerQT;
};

bool CvVideoWriter_QT_CPP::open( const char* filename, int fourcc,
								 double fps, CvSize frameSize, bool isColor ) {
	close();
	writerQT = icvCreateVideoWriter_QT( filename, fourcc, fps, frameSize, isColor );
	return writerQT != 0;
}

void CvVideoWriter_QT_CPP::close() {
	if ( writerQT ) {
		icvReleaseVideoWriter_QT( &writerQT );
		writerQT = 0;
	}
}

bool CvVideoWriter_QT_CPP::writeFrame( const IplImage* image ) {
	if ( !writerQT || !image ) {
		return false;
	}
	return icvWriteFrame_QT( writerQT, image ) >= 0;
}

CvVideoWriter* cvCreateVideoWriter_QT( const char* filename, int fourcc,
									   double fps, CvSize frameSize, int isColor ) {
	CvVideoWriter_QT_CPP* writer = new CvVideoWriter_QT_CPP;
	if ( writer->open( filename, fourcc, fps, frameSize, isColor != 0 )) {
		return writer;
	}
	delete writer;
	return 0;
}
